package ai.people.platform.jx.service.impl;

import ai.people.netmon.framework.domain.common.SimpleNameValueRespVO;
import ai.people.netmon.framework.domain.jx.AfricaTerrorismEvent;
import ai.people.netmon.framework.model.response.PageResponse;
import ai.people.platform.jx.algorithm.DataPoint;
import ai.people.platform.jx.algorithm.RegressionLine;
import ai.people.platform.jx.service.IAfricaTerrorismService;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author LiGuanNan
 * @date 2022/6/28 13:43
 */
@Slf4j
@Service
public class AfricaTerrorismServiceImpl implements IAfricaTerrorismService {

    @Value("${africa.terrorism.data.web.excel}")
    private String webExcelUrl;

    @Value("${africa.terrorism.data.event.excel}")
    private String eventExcelUrl;

    /**
     * 非洲国家地理位置json缓存
     */
    private String africaGeoJsonCache = null;

    /**
     * 非洲恐怖主义事件全量数据缓存
     */
    private Map<String, List<AfricaTerrorismEvent>> africaTerrorismEventMapCache = Maps.newLinkedHashMap();

    /**
     * 非洲恐怖主义事件预测线性回归方程集合
     */
    private Map<String, RegressionLine> africaTerrorismEventRegressionLine = Maps.newLinkedHashMap();

    //@PostConstruct
    public void init(){
        //初始化加载非洲地图json结构数据
        log.warn("初始化加载非洲地图json结构数据，begin...");
        getAfricaGeoJsonCache();
        log.warn("初始化加载非洲地图json结构数据，end...");
        //初始化加载非恐事件列表
        log.warn("初始化加载非恐事件列表，begin...");
        getAfricaTerrorismEventMapCache();
        log.warn("初始化加载非恐事件列表，end...");
        //初始化非洲恐怖主义事件预测线性回归方程集合
        log.warn("初始化非洲恐怖主义事件预测线性回归方程，begin...");
        getAfricaTerrorismEventRegressionLine();
        log.warn("初始化非洲恐怖主义事件预测线性回归方程，end...");
    }

    private synchronized Map<String, RegressionLine> getAfricaTerrorismEventRegressionLine() {
        if(!CollectionUtils.isEmpty(africaTerrorismEventRegressionLine)){
            return africaTerrorismEventRegressionLine;
        }

        africaTerrorismEventRegressionLine = doGetStringRegressionLineMap();
        return africaTerrorismEventRegressionLine;
    }

    private Map<String, RegressionLine> doGetStringRegressionLineMap() {
        Map<String, RegressionLine> map = Maps.newLinkedHashMap();
        africaTerrorismEventMapCache.keySet().forEach(key->{
            List<AfricaTerrorismEvent> africaTerrorismEvents = africaTerrorismEventMapCache.get(key);
            Map<String, List<AfricaTerrorismEvent>> collect = africaTerrorismEvents.stream().collect(Collectors.groupingBy(AfricaTerrorismEvent::getIyear));
            List<DataPoint> dataPoints = Lists.newArrayList();
            for(int i = 1970; i < 2018; i++){
                dataPoints.add(new DataPoint((float) i, (float) collect.getOrDefault(i + "", new ArrayList<>()).size()));
            }
            map.put(key, new RegressionLine(dataPoints));
        });
        return map;
    }

    private synchronized Map<String, List<AfricaTerrorismEvent>> getAfricaTerrorismEventMapCache() {
        if(!CollectionUtils.isEmpty(africaTerrorismEventMapCache)){
            return africaTerrorismEventMapCache;
        }
        africaTerrorismEventMapCache = doGetAfricaTerrorismEventMapCache();
        return africaTerrorismEventMapCache;
    }

    private Map<String, List<AfricaTerrorismEvent>> doGetAfricaTerrorismEventMapCache() {
        ExcelReader reader = null;
        List<AfricaTerrorismEvent> list = Lists.newArrayList();
        try {
            URL url = new URL(eventExcelUrl);
            URLConnection conn = url.openConnection();
            reader = ExcelUtil.getReader(conn.getInputStream());
            if (Objects.nonNull(reader)) {
                list = reader.read(0,1, AfricaTerrorismEvent.class);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if(reader != null){
                reader.close();
            }
        }
        return list.stream().collect(Collectors.groupingBy(AfricaTerrorismEvent::getCountry));
    }

    private synchronized String getAfricaGeoJsonCache() {
        if(StringUtils.isNotBlank(africaGeoJsonCache)){
            return africaGeoJsonCache;
        }
        africaGeoJsonCache = doGetAfricaGeoJsonCache();
        return africaGeoJsonCache;
    }

    private String doGetAfricaGeoJsonCache() {
        String lastStr = "";
        ClassPathResource classPathResource = new ClassPathResource("africa.geo.json");
        try (InputStream fileInputStream = classPathResource.getInputStream();
             InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
             BufferedReader reader = new BufferedReader(inputStreamReader)) {
            String tempString = null;
            while ((tempString = reader.readLine()) != null) {
                lastStr += tempString;
            }
        }catch (Exception e){
            log.error("非洲地图json加载失败");
            e.printStackTrace();
        }
        return lastStr;
    }

    /**
     * 估算事件数
     *
     * @param year    年份
     * @param country 国家
     * @return {@link Integer} 预估事件总数
     */
    @Override
    public Integer calWarning(Integer year, String country) {
        RegressionLine regressionLine = africaTerrorismEventRegressionLine.get(country);
        if(regressionLine == null){
            return 0;
        }
        return (int) (regressionLine.getA1() * year + regressionLine.getA0());
    }

    /**
     * 非洲恐怖主义事件数据源网站表格
     *
     * @return {@link List}<{@link Map}<{@link String}, {@link Object}>>
     */
    @Override
    public List<Map<String, Object>> webExcel() {
        ExcelReader reader = null;
        try {
            URL url = new URL(webExcelUrl);
            URLConnection conn = url.openConnection();
            reader = ExcelUtil.getReader(conn.getInputStream());
            if (Objects.nonNull(reader)) {
                return reader.readAll();
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if(reader != null){
                reader.close();
            }
        }
        return Lists.newArrayList();
    }

    /**
     * 非洲恐怖主义事件表格预览
     * @return {@link List}<{@link Map}<{@link String}, {@link Object}>>
     */
    @Override
    public PageResponse<AfricaTerrorismEvent> eventExcel(Integer pageNo, Integer pageSize, Integer beginYear, Integer endYear) {
        List<AfricaTerrorismEvent> africaTerrorismEvents = Lists.newArrayList();
        africaTerrorismEventMapCache.keySet().forEach(key->{
            africaTerrorismEvents.addAll(africaTerrorismEventMapCache.getOrDefault(key, Lists.newArrayList()));
        });
        return getAfricaTerrorismEventPageResponse(africaTerrorismEvents, pageSize, pageNo, beginYear, endYear);
    }

    private PageResponse<AfricaTerrorismEvent> getAfricaTerrorismEventPageResponse(List<AfricaTerrorismEvent> africaTerrorismEvents, Integer pageSize, Integer pageNo, Integer beginYear, Integer endYear) {
        if(!CollectionUtils.isEmpty(africaTerrorismEvents)){
            Stream<AfricaTerrorismEvent> africaTerrorismEventStream = africaTerrorismEvents.stream();
            if(beginYear != null){
                africaTerrorismEventStream = africaTerrorismEventStream.filter(o->Integer.parseInt(o.getIyear()) >= beginYear);
            }
            if(endYear != null){
                africaTerrorismEventStream = africaTerrorismEventStream.filter(o->Integer.parseInt(o.getIyear()) <= endYear);
            }
            africaTerrorismEvents = africaTerrorismEventStream.collect(Collectors.toList());
            List<AfricaTerrorismEvent> collect = africaTerrorismEvents.stream()
                    .sorted(Comparator.comparing(AfricaTerrorismEvent::getCountry).thenComparing(o -> (o.getIyear() + o.getImonth() + o.getIday())))
                    .skip((long) pageSize * (pageNo - 1))
                    .limit(pageSize)
                    .collect(Collectors.toList());
            int pages = (int) Math.ceil((double) africaTerrorismEvents.size() / (double) pageSize);
            return PageResponse.builder(collect, africaTerrorismEvents.size(), pages, pageNo);
        }
        return PageResponse.builderEmpty();
    }

    /**
     * 非洲恐怖袭击事件概览
     *
     * @return {@link JSONObject}
     */
    @Override
    public JSONObject overView() {
        if(!CollectionUtils.isEmpty(getAfricaTerrorismEventMapCache()) && !StringUtils.isBlank(getAfricaGeoJsonCache())){
            return setAndForecastEventCount();
        }
        return null;
    }

    /**
     * 分页查询事件列表
     *
     * @param pageNo   页面编号
     * @param pageSize 页面大小
     * @param country  国家
     * @return {@link PageResponse}<{@link AfricaTerrorismEvent}>
     */
    @Override
    public PageResponse<AfricaTerrorismEvent> queryEventPage(Integer pageNo, Integer pageSize, String country, Integer beginYear, Integer endYear) {
        if(!CollectionUtils.isEmpty(getAfricaTerrorismEventMapCache())){
            List<AfricaTerrorismEvent> africaTerrorismEvents = africaTerrorismEventMapCache.get(country);
            return getAfricaTerrorismEventPageResponse(africaTerrorismEvents, pageSize, pageNo, beginYear, endYear);
        }
        return PageResponse.builderEmpty();
    }

    /**
     * 非洲恐怖袭击事件统计数据
     *
     * @param country 国家
     * @return {@link List}<{@link SimpleNameValueRespVO}>
     */
    @Override
    public List<SimpleNameValueRespVO> statistics(String country) {
        List<AfricaTerrorismEvent> africaTerrorismEvents = africaTerrorismEventMapCache.get(country);
        Map<String, List<AfricaTerrorismEvent>> collect = africaTerrorismEvents.stream().collect(Collectors.groupingBy(AfricaTerrorismEvent::getIyear));
        List<SimpleNameValueRespVO> dataPoints = Lists.newArrayList();
        for(int i = 1970; i <= 2023; i++){
            if(i > 2017){
                dataPoints.add(new SimpleNameValueRespVO(i + "", calWarning(i, country)));
            }else {
                dataPoints.add(new SimpleNameValueRespVO(i + "", collect.getOrDefault(i + "", new ArrayList<>()).size()));
            }
        }
        return dataPoints;
    }

    /**
     * 设置并预判未来一年事件总数
     *
     * @return {@link JSONObject}
     */
    private JSONObject setAndForecastEventCount() {
        JSONObject africaGeoJsonObject = JSONObject.parseObject(africaGeoJsonCache);
        JSONArray features = africaGeoJsonObject.getJSONArray("features");
        features.forEach(o->{
            JSONObject jsonObject = (JSONObject) o;
            JSONObject properties = jsonObject.getJSONObject("properties");
            String country = properties.getString("name");
            properties.put("eventCount", africaTerrorismEventMapCache.getOrDefault(country,Lists.newArrayList()).size());
            int year = LocalDate.now().getYear() + 1;
            properties.put("forecastYear",year);
            properties.put("forecastCount", calWarning(year, country));
        });
        return africaGeoJsonObject;
    }
}
